// Package snapshotter implements the containerd snapshotter interface for smart snapshotter
package snapshotter

import (
	"context"
	"fmt"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/containerd/containerd/mount"
	"github.com/containerd/containerd/snapshots"
	cdstorage "github.com/containerd/containerd/snapshots/storage"
	"github.com/smart-snapshotter/pkg/cache"
	"github.com/smart-snapshotter/pkg/dedup"
	"github.com/smart-snapshotter/pkg/loader"
	"github.com/smart-snapshotter/pkg/storage"
	"github.com/smart-snapshotter/pkg/types"
)

// MetaStore interface for metadata operations
type MetaStore interface {
	CreateActive(ctx context.Context, key string, info snapshots.Info) error
	GetInfo(ctx context.Context, key string) (snapshots.Info, error)
	Update(ctx context.Context, key string, info snapshots.Info) error
	Remove(ctx context.Context, key string) error
	Walk(ctx context.Context, fn func(context.Context, snapshots.Info) error, filters ...string) error
	Close() error
}

// SimpleMetaStore implements a simple metadata store
type SimpleMetaStore struct {
	store *cdstorage.MetaStore
}

func (s *SimpleMetaStore) CreateActive(ctx context.Context, key string, info snapshots.Info) error {
	// For now, just store in memory
	return nil
}

func (s *SimpleMetaStore) GetInfo(ctx context.Context, key string) (snapshots.Info, error) {
	// For now, return empty info
	return snapshots.Info{Name: key}, nil
}

func (s *SimpleMetaStore) Update(ctx context.Context, key string, info snapshots.Info) error {
	// For now, just return nil
	return nil
}

func (s *SimpleMetaStore) Remove(ctx context.Context, key string) error {
	// For now, just return nil
	return nil
}

func (s *SimpleMetaStore) Walk(ctx context.Context, fn func(context.Context, snapshots.Info) error, filters ...string) error {
	// For now, just return nil
	return nil
}

func (s *SimpleMetaStore) Close() error {
	return s.store.Close()
}

// SmartSnapshotter implements the containerd snapshotter interface
type SmartSnapshotter struct {
	config    *types.Config
	storage   types.StorageBackend
	dedup     types.DedupEngine
	cache     types.Cache
	loader    types.Loader
	metastore MetaStore
	root      string
	mu        sync.RWMutex
}

// New creates a new smart snapshotter instance
func New(ctx context.Context, root string, config *types.Config) (*SmartSnapshotter, error) {
	if err := os.MkdirAll(root, 0755); err != nil {
		return nil, fmt.Errorf("failed to create root directory: %w", err)
	}

	// Initialize storage backend
	storageBackend, err := storage.NewBackend(storage.Config{
		Type:        config.Storage.Type,
		BasePath:    config.Storage.BasePath,
		MaxSize:     config.Storage.MaxSize,
		Compression: config.Storage.Compression,
		Encryption:  config.Storage.Encryption,
		BlockSize:   4096,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to create storage backend: %w", err)
	}

	// Initialize deduplication engine
	dedupEngine, err := dedup.NewEngine(dedup.Config{
		Enabled:       config.Dedup.Enabled,
		Algorithm:     config.Dedup.Algorithm,
		BlockSize:     int64(config.Dedup.BlockSize),
		MinFileSize:   config.Dedup.MinFileSize,
		HashAlgorithm: config.Dedup.HashAlgorithm,
		Storage:       &storageWrapper{backend: storageBackend},
	})
	if err != nil {
		return nil, fmt.Errorf("failed to create dedup engine: %w", err)
	}

	// Initialize cache
	cache, err := cache.New(cache.Config{
		Type:     config.Cache.Type,
		MaxSize:  config.Cache.MaxSize,
		TTL:      int64(config.Cache.TTL.Seconds()),
		Strategy: config.Cache.Strategy,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to create cache: %w", err)
	}

	// Initialize loader
	loader, err := loader.New(loader.Config{
		PrefetchEnabled: config.Loader.PrefetchEnabled,
		PrefetchDepth:   config.Loader.PrefetchDepth,
		MaxConcurrency:  config.Loader.MaxConcurrency,
		Timeout:         int64(config.Loader.Timeout.Seconds()),
	}, storageBackend, cache)
	if err != nil {
		return nil, fmt.Errorf("failed to create loader: %w", err)
	}

	// Initialize metadata store
	metastore, err := cdstorage.NewMetaStore(filepath.Join(root, "metadata.db"))
	if err != nil {
		return nil, fmt.Errorf("failed to create metadata store: %w", err)
	}

	// Create a simple metadata store wrapper
	metaWrapper := &SimpleMetaStore{store: metastore}

	return &SmartSnapshotter{
		config:    config,
		storage:   &storageWrapper{backend: storageBackend},
		dedup:     &dedupWrapper{engine: dedupEngine},
		cache:     &cacheWrapper{cache: cache},
		loader:    &loaderWrapper{loader: loader},
		metastore: metaWrapper,
		root:      root,
	}, nil
}

// Prepare creates an active (writable) snapshot
func (s *SmartSnapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	// Create active snapshot
	info := snapshots.Info{
		Name:   key,
		Parent: parent,
		Kind:   snapshots.KindActive,
		Labels: make(map[string]string),
	}

	// Apply options
	for _, opt := range opts {
		if err := opt(&info); err != nil {
			return nil, fmt.Errorf("failed to apply option: %w", err)
		}
	}

	// Create snapshot in metadata store
	if err := s.metastore.CreateActive(ctx, key, info); err != nil {
		return nil, fmt.Errorf("failed to create snapshot metadata: %w", err)
	}

	// Create snapshot directory
	snapshotPath := filepath.Join(s.root, "snapshots", key)
	if err := os.MkdirAll(snapshotPath, 0755); err != nil {
		return nil, fmt.Errorf("failed to create snapshot directory: %w", err)
	}

	// If parent exists, set up copy-on-write
	if parent != "" {
		parentPath := filepath.Join(s.root, "snapshots", parent)
		if err := s.setupCopyOnWrite(snapshotPath, parentPath); err != nil {
			return nil, fmt.Errorf("failed to setup copy-on-write: %w", err)
		}
	}

	// Return mount configuration
	return s.getMounts(snapshotPath, key, false), nil
}

// View creates a read-only active snapshot
func (s *SmartSnapshotter) View(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	// Create view snapshot
	info := snapshots.Info{
		Name:   key,
		Parent: parent,
		Kind:   snapshots.KindView,
		Labels: make(map[string]string),
	}

	// Apply options
	for _, opt := range opts {
		if err := opt(&info); err != nil {
			return nil, fmt.Errorf("failed to apply option: %w", err)
		}
	}

	// Create snapshot in metadata store
	if err := s.metastore.CreateActive(ctx, key, info); err != nil {
		return nil, fmt.Errorf("failed to create view metadata: %w", err)
	}

	// Create view directory
	viewPath := filepath.Join(s.root, "views", key)
	if err := os.MkdirAll(viewPath, 0755); err != nil {
		return nil, fmt.Errorf("failed to create view directory: %w", err)
	}

	// If parent exists, set up read-only mount
	if parent != "" {
		parentPath := filepath.Join(s.root, "snapshots", parent)
		if err := s.setupReadOnlyMount(viewPath, parentPath); err != nil {
			return nil, fmt.Errorf("failed to setup read-only mount: %w", err)
		}
	}

	// Return mount configuration
	return s.getMounts(viewPath, key, true), nil
}

// Mounts returns mount configuration for an active snapshot
func (s *SmartSnapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()

	// Check if it's an active snapshot
	snapshotPath := filepath.Join(s.root, "snapshots", key)
	if _, err := os.Stat(snapshotPath); err == nil {
		return s.getMounts(snapshotPath, key, false), nil
	}

	// Check if it's a view
	viewPath := filepath.Join(s.root, "views", key)
	if _, err := os.Stat(viewPath); err == nil {
		return s.getMounts(viewPath, key, true), nil
	}

	return nil, fmt.Errorf("snapshot %s not found", key)
}

// Commit converts an active snapshot to a committed snapshot
func (s *SmartSnapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	// Get active snapshot info
	info := snapshots.Info{
		Name:   key,
		Parent: "",
		Kind:   snapshots.KindActive,
		Labels: make(map[string]string),
	}

	// Apply commit options
	for _, opt := range opts {
		if err := opt(&info); err != nil {
			return fmt.Errorf("failed to apply commit option: %w", err)
		}
	}

	// Update snapshot info for commit
	info.Name = name
	info.Kind = snapshots.KindCommitted
	info.Created = time.Now()

	// Commit to metadata store
	if err := s.metastore.Update(ctx, info.Name, info); err != nil {
		return fmt.Errorf("failed to commit snapshot metadata: %w", err)
	}

	// Process snapshot data through deduplication engine
	snapshotPath := filepath.Join(s.root, "snapshots", key)
	if err := s.processSnapshotForDedup(ctx, name, snapshotPath); err != nil {
		return fmt.Errorf("failed to process snapshot for dedup: %w", err)
	}

	// Remove active snapshot
	if err := os.RemoveAll(snapshotPath); err != nil {
		return fmt.Errorf("failed to remove active snapshot: %w", err)
	}

	// Remove from views if exists
	viewPath := filepath.Join(s.root, "views", key)
	if err := os.RemoveAll(viewPath); err != nil && !os.IsNotExist(err) {
		return fmt.Errorf("failed to remove view: %w", err)
	}

	return nil
}

// Remove removes a snapshot
func (s *SmartSnapshotter) Remove(ctx context.Context, key string) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	// Get snapshot info
	info := snapshots.Info{
		Name:   key,
		Parent: "",
		Kind:   snapshots.KindActive,
		Labels: make(map[string]string),
	}

	_ = info // Use info to avoid unused variable error

	// Remove from metadata store
	if err := s.metastore.Remove(ctx, key); err != nil {
		return fmt.Errorf("failed to remove snapshot metadata: %w", err)
	}

	// Remove snapshot directories
	snapshotPath := filepath.Join(s.root, "snapshots", key)
	if err := os.RemoveAll(snapshotPath); err != nil && !os.IsNotExist(err) {
		return fmt.Errorf("failed to remove snapshot directory: %w", err)
	}

	viewPath := filepath.Join(s.root, "views", key)
	if err := os.RemoveAll(viewPath); err != nil && !os.IsNotExist(err) {
		return fmt.Errorf("failed to remove view directory: %w", err)
	}

	// Update deduplication references
	if err := s.removeSnapshotFromDedup(ctx, key); err != nil {
		return fmt.Errorf("failed to update dedup references: %w", err)
	}

	return nil
}

// processSnapshotForDedup processes a snapshot through the deduplication engine
func (s *SmartSnapshotter) processSnapshotForDedup(ctx context.Context, snapshotID string, path string) error {
	// For now, this is a placeholder implementation
	// In a full implementation, this would:
	// 1. Walk through all files in the snapshot
	// 2. Calculate hashes for deduplication
	// 3. Store deduplication metadata
	// 4. Update reference counts

	// Mock implementation for testing
	return nil
}

// removeSnapshotFromDedup removes snapshot data from deduplication engine
func (s *SmartSnapshotter) removeSnapshotFromDedup(ctx context.Context, snapshotID string) error {
	// For now, this is a placeholder implementation
	// In a full implementation, this would:
	// 1. Update reference counts for deduplicated data
	// 2. Clean up unused deduplication entries
	// 3. Update statistics

	// Mock implementation for testing
	return nil
}

// Stat returns metadata about a snapshot
func (s *SmartSnapshotter) Stat(ctx context.Context, key string) (snapshots.Info, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()

	return s.metastore.GetInfo(ctx, key)
}

// Update updates snapshot metadata
func (s *SmartSnapshotter) Update(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	if err := s.metastore.Update(ctx, info.Name, info); err != nil {
		return snapshots.Info{}, fmt.Errorf("failed to update snapshot metadata: %w", err)
	}

	return info, nil
}

// Usage returns disk usage information for a snapshot
func (s *SmartSnapshotter) Usage(ctx context.Context, key string) (snapshots.Usage, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()

	// Get snapshot info
	info := snapshots.Info{
		Name:   key,
		Parent: "",
		Kind:   snapshots.KindActive,
		Labels: make(map[string]string),
	}

	// Calculate usage based on snapshot kind
	var usage snapshots.Usage
	var err error
	switch info.Kind {
	case snapshots.KindActive:
		snapshotPath := filepath.Join(s.root, "snapshots", key)
		usage, err = s.calculateUsage(snapshotPath)
	case snapshots.KindView:
		viewPath := filepath.Join(s.root, "views", key)
		usage, err = s.calculateUsage(viewPath)
	case snapshots.KindCommitted:
		// For committed snapshots, get usage from dedup engine
		usageInfo, err := s.dedup.GetSnapshotUsage(ctx, key)
		if err != nil {
			return snapshots.Usage{}, fmt.Errorf("failed to get snapshot usage: %w", err)
		}
		usage = snapshots.Usage{
			Size:   usageInfo.Size,
			Inodes: usageInfo.Inodes,
		}
	default:
		return snapshots.Usage{}, fmt.Errorf("unknown snapshot kind: %v", info.Kind)
	}

	if err != nil {
		return snapshots.Usage{}, fmt.Errorf("failed to calculate usage: %w", err)
	}

	return usage, nil
}

// Walk iterates through all snapshots
func (s *SmartSnapshotter) Walk(ctx context.Context, fn func(context.Context, snapshots.Info) error, filters ...string) error {
	s.mu.RLock()
	defer s.mu.RUnlock()

	// For now, return empty walk
	return nil
}

// Close closes the snapshotter
func (s *SmartSnapshotter) Close() error {
	s.mu.Lock()
	defer s.mu.Unlock()

	var errs []error

	// Close dedup engine
	// if err := s.dedup.Close(); err != nil {
	// 	errs = append(errs, fmt.Errorf("failed to close dedup engine: %w", err))
	// }

	// Close cache
	// if err := s.cache.Close(); err != nil {
	// 	errs = append(errs, fmt.Errorf("failed to close cache: %w", err))
	// }

	// Close loader
	// if err := s.loader.Close(); err != nil {
	// 	errs = append(errs, fmt.Errorf("failed to close loader: %w", err))
	// }

	// Close metadata store
	if err := s.metastore.Close(); err != nil {
		errs = append(errs, fmt.Errorf("failed to close metadata store: %w", err))
	}

	if len(errs) > 0 {
		return fmt.Errorf("errors closing snapshotter: %v", errs)
	}

	return nil
}

// Helper methods

func (s *SmartSnapshotter) getMounts(path, key string, readonly bool) []mount.Mount {
	// Implementation depends on storage backend type
	switch s.config.Storage.Type {
	case "overlay":
		return s.getOverlayMounts(path, key, readonly)
	case "erofs":
		return s.getEROFSMounts(path, key, readonly)
	case "composefs":
		return s.getComposeFSMounts(path, key, readonly)
	default:
		return []mount.Mount{}
	}
}

func (s *SmartSnapshotter) getOverlayMounts(path, key string, readonly bool) []mount.Mount {
	// OverlayFS mount configuration
	options := []string{"index=off"}
	if readonly {
		options = append(options, "ro")
	}

	return []mount.Mount{
		{
			Type:    "overlay",
			Source:  "overlay",
			Options: append(options, fmt.Sprintf("lowerdir=%s", path)),
		},
	}
}

func (s *SmartSnapshotter) getEROFSMounts(path, key string, readonly bool) []mount.Mount {
	// EROFS mount configuration
	return []mount.Mount{
		{
			Type:   "erofs",
			Source: path,
			Options: []string{},
		},
	}
}

func (s *SmartSnapshotter) getComposeFSMounts(path, key string, readonly bool) []mount.Mount {
	// ComposeFS mount configuration
	options := []string{}
	if readonly {
		options = append(options, "ro")
	}

	return []mount.Mount{
		{
			Type:    "composefs",
			Source:  path,
			Options: options,
		},
	}
}

func (s *SmartSnapshotter) setupCopyOnWrite(target, parent string) error {
	// Setup copy-on-write for the target directory
	// This is storage backend specific
	return nil
}

func (s *SmartSnapshotter) setupReadOnlyMount(target, parent string) error {
	// Setup read-only mount for view snapshots
	// This is storage backend specific
	return nil
}


func (s *SmartSnapshotter) calculateUsage(path string) (snapshots.Usage, error) {
	// Calculate disk usage for a directory
	var usage snapshots.Usage
	err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !info.IsDir() {
			usage.Size += info.Size()
			usage.Inodes++
		}
		return nil
	})
	return usage, err
}

// Wrapper types to adapt interfaces

type storageWrapper struct {
	backend storage.Backend
}

func (w *storageWrapper) StoreFile(ctx context.Context, hash string, data []byte) error {
	return w.backend.StoreFile(ctx, hash, data)
}

func (w *storageWrapper) LoadFile(ctx context.Context, hash string) ([]byte, error) {
	return w.backend.LoadFile(ctx, hash)
}

func (w *storageWrapper) DeleteFile(ctx context.Context, hash string) error {
	return w.backend.DeleteFile(ctx, hash)
}

func (w *storageWrapper) FileExists(ctx context.Context, hash string) (bool, error) {
	return w.backend.FileExists(ctx, hash)
}

func (w *storageWrapper) StoreBlock(ctx context.Context, hash string, data []byte) error {
	return w.backend.StoreBlock(ctx, hash, data)
}

func (w *storageWrapper) LoadBlock(ctx context.Context, hash string) ([]byte, error) {
	return w.backend.LoadBlock(ctx, hash)
}

func (w *storageWrapper) DeleteBlock(ctx context.Context, hash string) error {
	return w.backend.DeleteBlock(ctx, hash)
}

func (w *storageWrapper) BlockExists(ctx context.Context, hash string) (bool, error) {
	return w.backend.BlockExists(ctx, hash)
}

func (w *storageWrapper) StoreMetadata(ctx context.Context, key string, metadata []byte) error {
	return w.backend.StoreMetadata(ctx, key, map[string]string{})
}

func (w *storageWrapper) LoadMetadata(ctx context.Context, key string) ([]byte, error) {
	return nil, fmt.Errorf("not implemented")
}

func (w *storageWrapper) DeleteMetadata(ctx context.Context, key string) error {
	return w.backend.DeleteMetadata(ctx, key)
}

func (w *storageWrapper) Health(ctx context.Context) error {
	return nil
}

func (w *storageWrapper) Cleanup(ctx context.Context) error {
	return w.backend.Cleanup(ctx)
}

func (w *storageWrapper) GetStats(ctx context.Context) (storage.StorageStats, error) {
	return w.backend.GetStats(ctx)
}

func (w *storageWrapper) Close() error {
	return w.backend.Close()
}

type dedupWrapper struct {
	engine dedup.Engine
}

func (w *dedupWrapper) AddFile(ctx context.Context, path string, data []byte) (string, error) {
	return w.engine.AddFile(ctx, path, data)
}

func (w *dedupWrapper) GetFile(ctx context.Context, hash string) ([]byte, error) {
	return w.engine.GetFile(ctx, hash)
}

func (w *dedupWrapper) DeleteFile(ctx context.Context, hash string) error {
	return w.engine.DeleteFile(ctx, hash)
}

func (w *dedupWrapper) FileExists(ctx context.Context, hash string) (bool, error) {
	return w.engine.FileExists(ctx, hash)
}

func (w *dedupWrapper) AddBlock(ctx context.Context, data []byte) (string, error) {
	return w.engine.AddBlock(ctx, data)
}

func (w *dedupWrapper) GetBlock(ctx context.Context, hash string) ([]byte, error) {
	return w.engine.GetBlock(ctx, hash)
}

func (w *dedupWrapper) DeleteBlock(ctx context.Context, hash string) error {
	return w.engine.DeleteBlock(ctx, hash)
}

func (w *dedupWrapper) BlockExists(ctx context.Context, hash string) (bool, error) {
	return w.engine.BlockExists(ctx, hash)
}

func (w *dedupWrapper) ProcessBlocks(ctx context.Context, data []byte) ([]types.BlockInfo, error) {
	blocks, err := w.engine.ProcessBlocks(ctx, data)
	if err != nil {
		return nil, err
	}
	result := make([]types.BlockInfo, len(blocks))
	for i, block := range blocks {
		result[i] = types.BlockInfo{
			Hash:      block.Hash,
			Size:      block.Size,
			Offset:    block.Offset,
			Reference: block.Reference,
		}
	}
	return result, nil
}

func (w *dedupWrapper) ReconstructFile(ctx context.Context, blocks []types.BlockInfo) ([]byte, error) {
	engineBlocks := make([]dedup.BlockInfo, len(blocks))
	for i, block := range blocks {
		engineBlocks[i] = dedup.BlockInfo{
			Hash:      block.Hash,
			Size:      block.Size,
			Offset:    block.Offset,
			Reference: block.Reference,
		}
	}
	return w.engine.ReconstructFile(ctx, engineBlocks)
}

func (w *dedupWrapper) ProcessSnapshot(ctx context.Context, snapshotID string, path string) error {
	return w.engine.ProcessSnapshot(ctx, snapshotID, path)
}

func (w *dedupWrapper) RemoveSnapshot(ctx context.Context, snapshotID string) error {
	return w.engine.RemoveSnapshot(ctx, snapshotID)
}

func (w *dedupWrapper) GetSnapshotUsage(ctx context.Context, snapshotID string) (types.Usage, error) {
	usage, err := w.engine.GetSnapshotUsage(ctx, snapshotID)
	if err != nil {
		return types.Usage{}, err
	}
	return types.Usage{
		Size:   usage.Size,
		Inodes: usage.Inodes,
	}, nil
}

func (w *dedupWrapper) GetStats(ctx context.Context) (types.DedupStats, error) {
	stats, err := w.engine.GetStats(ctx)
	if err != nil {
		return types.DedupStats{}, err
	}
	return types.DedupStats{
		TotalFiles:      stats.TotalFiles,
		UniqueFiles:     stats.UniqueFiles,
		TotalBlocks:     stats.TotalBlocks,
		UniqueBlocks:    stats.UniqueBlocks,
		SpaceSaved:      stats.SavedSpace,
		DedupRatio:      stats.DeduplicationRatio,
		LastUpdated:     time.Now(),
	}, nil
}

func (w *dedupWrapper) ResetStats(ctx context.Context) error {
	return nil
}

func (w *dedupWrapper) Close() error {
	return w.engine.Close()
}

type cacheWrapper struct {
	cache cache.Cache
}

func (w *cacheWrapper) Get(ctx context.Context, key string) ([]byte, error) {
	return w.cache.Get(ctx, key)
}

func (w *cacheWrapper) Set(ctx context.Context, key string, value []byte, ttl time.Duration) error {
	return w.cache.Set(ctx, key, value, ttl)
}

func (w *cacheWrapper) Delete(ctx context.Context, key string) error {
	return w.cache.Delete(ctx, key)
}

func (w *cacheWrapper) Exists(ctx context.Context, key string) (bool, error) {
	return w.cache.Exists(ctx, key)
}

func (w *cacheWrapper) Clear(ctx context.Context) error {
	return w.cache.Clear(ctx)
}

func (w *cacheWrapper) GetStats(ctx context.Context) (types.CacheStats, error) {
	stats, err := w.cache.GetStats(ctx)
	if err != nil {
		return types.CacheStats{}, err
	}
	return types.CacheStats{
		Hits:      stats.Hits,
		Misses:    stats.Misses,
		Evictions: stats.Evictions,
		Size:      stats.Size,
		Capacity:  stats.Capacity,
		HitRatio:  stats.HitRatio,
	}, nil
}

type loaderWrapper struct {
	loader loader.Loader
}

func (w *loaderWrapper) Load(ctx context.Context, key string) ([]byte, error) {
	return w.loader.Load(ctx, key)
}

func (w *loaderWrapper) Prefetch(ctx context.Context, keys []string) error {
	return w.loader.Prefetch(ctx, keys)
}

func (w *loaderWrapper) GetProgress(ctx context.Context, key string) (types.LoadingProgress, error) {
	progress, err := w.loader.GetProgress(ctx, key)
	if err != nil {
		return types.LoadingProgress{}, err
	}
	return types.LoadingProgress{
		Key:         progress.Key,
		Status:      progress.Status,
		BytesLoaded: progress.BytesLoaded,
		BytesTotal:  progress.BytesTotal,
		StartTime:   progress.StartTime,
		EndTime:     progress.EndTime,
		Error:       progress.Error,
	}, nil
}

func (w *loaderWrapper) GetStats(ctx context.Context) (types.LoaderStats, error) {
	stats, err := w.loader.GetStats(ctx)
	if err != nil {
		return types.LoaderStats{}, err
	}
	return types.LoaderStats{
		TotalRequests:    stats.TotalRequests,
		CacheHits:        stats.CacheHits,
		CacheMisses:      stats.CacheMisses,
		PrefetchRequests: stats.PrefetchRequests,
		ActiveDownloads:  stats.ActiveDownloads,
		TotalBytesLoaded: stats.TotalBytesLoaded,
		AverageLoadTime:  stats.AverageLoadTime,
	}, nil
}